/*
*    Qizx/open 3.0 beta
*
* This code is the open-source version of Qizx.
* Copyright (C) 2004-2009 Axyana Software -- All rights reserved.
*
* The contents of this file are subject to the Mozilla Public License 
*  Version 1.1 (the "License"); you may not use this file except in 
*  compliance with the License. You may obtain a copy of the License at
*  http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
*  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
*  for the specific language governing rights and limitations under the
*  License.
*
* The Initial Developer of the Original Code is Xavier Franc - Axyana Software.
*
*/
using System;
using System.Qizx.Api;
using XQDate = System.Qizx.Api.Util.XQDate;
using XQDateTime = System.Qizx.Api.Util.XQDateTime;
using XQDateTimeBase = System.Qizx.Api.Util.XQDateTimeBase;
using DateTimeException = System.Qizx.Api.Util.XQDateTimeException;
using Duration = System.Qizx.Api.Util.XQDuration;
using Comparison = System.Qizx.Util.Basic.Comparison;
using BasicNode = System.Qizx.Xdm.BasicNode;
using Conversion = System.Qizx.Xdm.Conversion;
using NodePullStream = System.Qizx.Xdm.NodePullStream;
using ArraySequence = System.Qizx.XQuery.Dt.ArraySequence;
using Atomizer = System.Qizx.XQuery.Dt.Atomizer;
using SingleItem = System.Qizx.XQuery.Dt.SingleItem;
using UntypedAtomicType = System.Qizx.XQuery.Dt.UntypedAtomicType;
using EmptyException = System.Qizx.XQuery.Impl.EmptyException;
using ErrorValue = System.Qizx.XQuery.Impl.ErrorValue;
namespace System.Qizx.XQuery {

    /// <summary> Default Value implementation.
    /// The <code>as<i>Type</i>()</code> methods implemented by this class
    /// always throw errors. Subclasses must redefine these methods according to the
    /// values held.
    /// </summary>
    public abstract class BaseValue : XQValue {
        virtual public ItemType ValueType {
            get {
                return itemType;
            }

        }
        virtual public XQItemType ItemType {
            get {
                return itemType;
            }

        }
        virtual public bool ValueAsBoolean {
            // CAUTION: asItem() should not return 'this' : else possible infinite loop
            get {
                throw NotConvertibleTo("boolean");
            }

        }
        virtual public long ValueAsInteger {
            get {
                throw NotConvertibleTo("integer");
            }

        }
        virtual public System.Decimal ValueAsDecimal {
            get {
                throw NotConvertibleTo("decimal");
            }

        }
        virtual public float ValueAsFloat {
            get {
                throw NotConvertibleTo("float");
            }

        }
        virtual public double ValueAsDouble {
            get {
                throw NotConvertibleTo("double");
            }

        }
        virtual public string ValueAsString {
            get {
                throw NotConvertibleTo("string");
            }

        }
        virtual public QName ValueAsQName {
            get {
                throw NotConvertibleTo("QName");
            }

        }
        virtual public Duration Duration {
            get {
                throw NotConvertibleTo("duration");
            }

        }
        virtual public XQDateTimeBase ValueAsMoment {
            get {
                throw NotConvertibleTo("date or time");
            }

        }
        virtual public XQDate ValueAsDate {
            get {
                return XQDate.parseDate(ValueAsString.Trim());
            }

        }
        virtual public XQDateTime ValueAsDateTime {
            get {
                return XQDateTime.ParseDateTime(ValueAsString.Trim());
            }

        }
        virtual public System.Object Object {
            get {
                return ValueAsString; // fallback
            }

        }
        virtual public bool Lazy {
            set {
                // ignored
            }

        }
        public abstract System.Qizx.XQuery.XQItem Item { get; }
        // CAUTION: asItem() should not return 'this' : else possible infinite loop
        protected internal XQItemType itemType;

        public virtual bool NextCollection() {
            return Next();
        }

        public virtual XQItem AsAtom() {
            return Item;
        }

        private EvaluationException NotConvertibleTo(string typeName) {
            try {
                ItemType type = ValueType;
                return new EvaluationException(Conversion.ERR_CAST, "required type xs:" + typeName + ", cannot accept actual type " + type);
            } catch (EvaluationException e) {
                return e; // / oops
            }
        }

        public virtual Node GetNode() {
            throw NotConvertibleTo("node");
        }

        public virtual bool IsNode {
            get { return false; }
        }

        public virtual BasicNode GetBasicNode() {
            throw new XQTypeException("sequence item is not a node");
        }

        // Default implem for leaf iterators
        public virtual double GetFulltextScore(Item item) {
            System.Console.Error.WriteLine("OOPS BaseValue.getFulltextScore : "); //rub
            return -1;
        }


        // Used in compareTo (not sorts)
        public static int Compare(long diff) {
            return diff < 0 ? Comparison.LT : (diff > 0 ? Comparison.GT : Comparison.EQ);
        }

        // Used in compareTo (not sort)
        public static int compare(double d1, double d2) {
            if (d1 != d1 || d2 != d2)
                // NaN
                return Comparison.FAIL;
            return d1 < d2 ? Comparison.LT : (d1 > d2 ? Comparison.GT : Comparison.EQ);
        }

        // Used in sort
        public static int compare(double d1, double d2, bool emptyGreatest) {
            if (d1 != d1)
                // NaN
                return emptyGreatest ? Comparison.GT : Comparison.LT;
            if (d2 != d2)
                return emptyGreatest ? Comparison.LT : Comparison.GT;
            return d1 < d2 ? Comparison.LT : (d1 > d2 ? Comparison.GT : Comparison.EQ);
        }

        public virtual int CompareTo(XQItem that, ComparisonContext context, int flags) {
            return UntypedAtomicType.comparison(this, that, context, flags);
        }

        public virtual bool DeepEquals(XQItem item, ComparisonContext context) {
            try {
                return CompareTo(item, context, 0) == 0;
            } catch (EvaluationException e) {
                return false;
            }
        }

        public virtual XQValue CheckTypeExpand(XQType type, EvalContext context, bool conversion, bool build) {
            XQItemType reqItemType = (type == null) ? null : type.itemType();

            if (!this.Next()) {
                if (type == null || type.isOptional())
                    return System.Qizx.XQuery.XQValues.Empty; // NOT "return this" ! (FIX)
                // otherwise error:
                return System.Qizx.XQuery.XQValues.TOO_SHORT;
            }
            ArraySequence resSeq = null;
            XQItem resItem = null;
            int count = 0;
            do {
                XQItem item = this.Item;
                XQItemType itemType = item.ItemType;
                if (reqItemType != null && !reqItemType.acceptsItem(item)) {
                    // still a chance if 'conversion':
                    bool ok = false;
                    if (conversion) {
                        // atomize to single item (if Node)
                        // Actually atomization should be applied to whole sequence
                        // IF itemType is atomic
                        item = Atomizer.ToSingleAtom(item);
                        if (reqItemType.promotable(item.ItemType)) {
                            item = reqItemType.cast(item, context);
                            ok = true;
                        }
                    }
                    if (!ok) {
                        return new ErrorValue(reqItemType + " cannot accept item of type " + itemType);
                    }
                }
                ++count;
                if (build)
                    if (resItem == null)
                        resItem = item;
                    else {
                        if (resSeq == null) {
                            resSeq = new ArraySequence(2, this);
                            resSeq.addItem(resItem);
                        }
                        resSeq.addItem(item);
                    }
            }
            while (this.Next());

            if (resSeq != null)
                resSeq.pack();
            XQValue result = build ? (resSeq != null ? (XQValue)resSeq : new SingleItem(resItem)) : this.BornAgain();
            if (type == null)
                return result;
            switch (type.Occurrence) {

                case System.Qizx.Api.SequenceState.OCC_ZERO_OR_ONE:
                    if (count <= 1)
                        return result;
                    return System.Qizx.XQuery.XQValues.TOO_LONG;

                case System.Qizx.Api.SequenceState.OCC_ZERO_OR_MORE:
                    return result;

                case System.Qizx.Api.SequenceState.OCC_ONE_OR_MORE:
                    if (count >= 1)
                        return result;
                    return System.Qizx.XQuery.XQValues.TOO_SHORT;

                default:  // one
                    if (count == 1)
                        return result;
                    return (count == 0) ? System.Qizx.XQuery.XQValues.TOO_SHORT : System.Qizx.XQuery.XQValues.TOO_LONG;

            }
        }

        public virtual bool WorthExpanding() {
            return true; // default behaviour
        }

        // default: can be optimized by some iterators
        public virtual long QuickCount(EvalContext context) {
            try {
                // lazy is only for avoiding building DM
                Lazy = true;
                int cnt = 0;
                for (; this.Next(); ) {
                    if (context != null && (cnt & 31) == 0)
                        context.at(null);
                    ++cnt;
                }
                return cnt;
            } catch (EmptyException e) {
                return 0;
            } finally {
                Lazy = false; // useful!
            }
        }

        public virtual int DoSkip(int count) {
            int skipped = 0;
            try {
                // lazy is only for avoiding accessing DM
                Lazy = true;

                for (; --count >= 0 && this.Next(); )
                    ++skipped;
            } catch (EmptyException ignored) {
                ;
            } finally {
                Lazy = false; // useful!
            }
            return skipped;
        }

        public virtual bool HasQuickIndex() {
            return false;
        }

        public virtual XQItem QuickIndex(long index) {
            return null;
        }

        public virtual void Export(XMLPushStream writer) {
            if (IsNode)
                writer.putNodeCopy(GetNode(), 0);
            else
                writer.putText(ValueAsString);
        }

        public virtual XMLPullStream ExportNode() {
            if (!IsNode)
                throw new EvaluationException("node item required");
            return new NodePullStream(GetNode());
        }

        public virtual void Close() {
            // reserved for future use (SQL results)
        }
        public abstract System.Qizx.XQuery.XQValue BornAgain();
        public abstract bool Next();
    }
}